/*
 * Copyright (c) 2018-2024 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "common.h"
#include "quickpool.h"
#include "qp.handler.h"

enum catalog {
    CATALOG_CLUSTER = 0,
    CATALOG_NODE,
    CATALOG_RESOURCE,
    CATALOG_QUEUE,
    CATALOG_USER,
    CATALOG_JOB,
    CATALOG_JM,
    CATALOG_JS,
    CATALOG_FILE,
    CATALOG_VERSION,   
    CATALOG_HELP,    
    CATALOG_CNT,
};

//align with the catalog enum
struct catalogEntry {
    const char *catalog;
    const char *description;
};

struct catalogEntry catalogList[] = {
    {"cluster", "Show cluster information"},
    {"node", "Show node information, or control nodes"},
    {"resource", "Show resource information"},
    {"queue", "Show queue information, or control queues"},
    {"user", "Show user and user group information"},
    {"job", "Submit, control jobs or show job information"},
    {"jm", "Control JM daemon"},
    {"js", "Control JS daemon"},
    {"file", "Copy file"},
    {"version", "Show version"},
    {"help", "Show help messages"},
    {NULL, NULL}
};

typedef int (*opHandler)(int, char**);

struct opEntry {
    const char *operation;
    const char *description;
    opHandler handler;
};

struct opEntry clusterOpTbl[] = {
    {"list", "Display cluster information", cmd_list_clusters},
    {"info", "Display cluster name, version and master name", cmd_cluster_info},
    {"resource", "Display resource information and node type/model", cmd_list_resources},
    {NULL, NULL}
}; 

struct opEntry jmOpTbl[] = {
    {"start", "Start JM daemon", startup},
    {"stop", "Stop JM daemon", jm_stop_handler},
    {"restart", "Restart JM daemon", jm_restart_handler},
    {"debug", "Debug JM daemon", jm_debug_handler},
    {"time", "Debug JM timing messages", jm_time_handler},
    {"checkconfig", "Check JM configuration files", jm_checkconf_handler},
    {"reconfig", "Reconfigure JM daemon", jm_reconfig_handler},
    {NULL, NULL}
};

struct opEntry jsOpTbl[] = {
    {"restart", "Restart JS daemon", js_restart_handler},
    {"hist", "JS history", js_hist_handler},
    {"debug", "Debug JS daemon", js_debug_handler},
    {"time", "Debug JS timing messages", js_time_handler},
    {"showconfig", "Display JS configurations",  cmd_show_settings},
    {"checkconfig", "Check JS configuration files", js_checkconf_handler},
    {"reconfig", "Reconfigure JS daemon", js_reconfig_handler},
    {NULL, NULL}
};

struct opEntry nodeOpTbl[] = {
    {"list", "Display node information in scheduling system", cmd_list_nodes},
    {"info", "Display node static information", cmd_node_info},
    {"load", "Display load information", cmd_node_load},
    {"group", "Display host group information", node_group_handler},
    {"open", "Open nodes", node_open_handler},
    {"close", "Close nodes", node_close_handler},
    {"add", "Add node into cluster", cmd_add_node},
    {"remove", "Remote node from cluster", cmd_remove_node}, 
    {"hist", "Display node operation history", node_hist_handler},
    {NULL, NULL}
};

struct opEntry jobOpTbl[] = {
    {"list", "Display job information", cmd_list_jobs},
    {"sub", "Submit a job", cmd_submit_job},
    {"mod", "Modify job submission options", cmd_modify_job},
    {"hist", "Display the history of jobs", cmd_job_records},
    {"switch", "Switch unfinished jobs from a queue to another", cmd_switch_job},
    {"kill", "Kill jobs", cmd_signal_job},
    {"chkpnt", "CheckPoint a job", cmd_signal_job},
    {"restart", "Restart a checkpoint job", cmd_submit_job},
    {"stop", "Suspend unfinished jobs", cmd_signal_job},
    {"resume", "Resume suspended jobs", cmd_signal_job},
    {"run", "Force a job to run", cmd_run_job},
    {"top", "Move a pending job to the top in the queue", cmd_move_job},
    {"bot", "Move a pending job to the bottom in the queue", cmd_move_job},
    {"mig", "Migrate rerunnable jobs", cmd_migrate_job},
    {"requeue", "Kill and requeue a job", cmd_requeue_job},
    {"read", "Read messages from a job", cmd_read_message},
    /* {"peek", "Display the stdout and stderr output of a unfinished job", cmd_peek_job}, */
    {"post", "Send message to a job", cmd_post_message},
    {"group", "Display job group information", cmd_list_jobgroups},
    {"gadd", "Create a job group", cmd_add_jobgroup},
    {"gdel", "Delete a job group", cmd_delete_jobgroup},
    {"gmod", "Modify a job group", cmd_modify_jobgroup},
    {"dep", "Display job dependency information", cmd_job_dependency},
    {NULL, NULL}
};

struct opEntry queueOpTbl[] = {
    {"list", "Display queue information", cmd_list_queues},
    {"open", "Open queues", queue_open_handler},
    {"close", "Close queues", queue_close_handler},
    {"act", "Activate queues", queue_active_handler},
    {"inact", "Inactivate queues", queue_inact_handler},
    {"hist", "Display queue operation history", queue_hist_handler},
    {"nodeadd", "Add node into queue", queue_nodeadd_handler},
    {"noderemove", "Remote node from queue", queue_noderemove_handler}, 
    {"nodeupdate", "Update queue node list", queue_nodeupdate_handler},     
    {NULL, NULL}
};

struct opEntry userOpTbl[] = {
    {"list", "Display user information", cmd_list_users},
    {"group", "Display user group information", user_group_handler},
    {NULL, NULL}
};

struct opEntry resourceOpTbl[] = {
    {"info", "Display resource information", cmd_resource_info},
    {NULL, NULL}
};

struct opEntry fileOpTbl[] = {
    {"rcp", "Copy job execfile", cmd_file_copy},
    {NULL, NULL}
};

struct opEntry dummyOpTbl[] = {
    {NULL, NULL}
};

struct opEntry *opTbls[CATALOG_CNT];
extern char *g_masterlist;

static void init_operate_table() {
    opTbls[CATALOG_CLUSTER] = clusterOpTbl;
    opTbls[CATALOG_JM] = jmOpTbl;
    opTbls[CATALOG_JS] = jsOpTbl;
    opTbls[CATALOG_NODE] = nodeOpTbl;
    opTbls[CATALOG_USER] = userOpTbl;
    opTbls[CATALOG_JOB] = jobOpTbl;
    opTbls[CATALOG_QUEUE] = queueOpTbl;
    opTbls[CATALOG_RESOURCE] = resourceOpTbl;
    opTbls[CATALOG_FILE] = fileOpTbl;
    opTbls[CATALOG_VERSION] = dummyOpTbl;    
    opTbls[CATALOG_HELP] = dummyOpTbl;    
}

static void print_qp_usage(char *);
static void print_catalogs();
static void print_operations(int);
static int get_catalog_index(char *, struct catalogEntry[]);
static int get_operate_index(char *, struct opEntry *);

int
main(int argc, char **argv, char **environ)
{
    int i;
    if (2 >= argc) {
        for (i=0; i<argc; i++) {
            if (0 == strcmp(argv[i], "-V") || 0 == strcmp(argv[i], "--version")) {
                fputs(_QP_VERSION_, stderr);
                exit(0);
            }
        }
    }

    init_operate_table();

    gf_set_ignorewarn(WARNING);
    if (gf_qp_init("qp") < 0) {
        exit(-1);
    }
    gf_set_ignorewarn(NOLOG);

    if (argc == 1) {
        //interactive mode, implement later if needed
        print_qp_usage(argv[0]);
        print_catalogs();
        return 0;
    }

    int catalogIdx, operationIdx;
    if ((catalogIdx = get_catalog_index(argv[1], catalogList)) < 0) {
        fprintf(stderr, "Invalid catalog: %s\n", argv[1]);
        print_catalogs();

        exit(-1);
    }
    
    //print version
    if (catalogIdx == CATALOG_VERSION) {
        fputs(_QP_VERSION_, stderr);
        return 0;
    }
    
    //handle help subcommand
    if (catalogIdx == CATALOG_HELP) {
        print_qp_usage(argv[0]);
        print_catalogs();
        exit(0);
    }

    if (argc < 3) {
        fprintf(stderr, "Operation not specified.\n");
        print_qp_usage(argv[0]);
        print_operations(catalogIdx);
        exit(-1);
    }

    if ((operationIdx = get_operate_index(argv[2], opTbls[catalogIdx]) )< 0) {
        fprintf(stderr, "Invalid operation: %s\n", argv[2]);
        print_operations(catalogIdx);
        exit(-1);
    }

    opHandler handler = opTbls[catalogIdx][operationIdx].handler;
    if (handler == NULL) {
        //safe guard, should not get in this
        fprintf(stderr, "%s: command handler is NULL.", __func__);
        return -1;
    }

    gf_set_hostfile(commandParams[CMD_CONFDIR].string_value);

    return handler(argc - 2, argv + 2);
} // end function main

/* ------------------------------------------------
 *
 *  below are static functions used in this file
 *
 * ------------------------------------------------ */

static int
get_catalog_index(char *catalog, struct catalogEntry cataloglist[])
{
    int i;
    for (i = 0; cataloglist[i].catalog != NULL; i++) {
        if (0 == strcmp(catalog, cataloglist[i].catalog)) {
            return i;
        }
    }

    return -1;
} // end function get_catalog_index

static int
get_operate_index(char *operation, struct opEntry *optable)
{
    int i;
    for (i = 0; optable[i].operation != NULL; i++) {
        if (strcmp(operation, optable[i].operation) == 0) {
            return i;
        }
    }

    return -1;
} // end function get_operate_index

static void
print_qp_usage(char *name)
{
    fprintf(stdout, "Usage: %s catalog operation [options] [arguments]\n", name);

    return;
} // end function print_qp_usage

static void
print_catalogs()
{
    fprintf(stderr, "Valid catalogs are:\n");

    int i;
    for (i = 0; catalogList[i].catalog != NULL; i++) {
        fprintf(stderr, "    %-12.12s%s\n", catalogList[i].catalog, catalogList[i].description);
    }

    return;
} // end function print_catalogs

static
void print_operations(int idx)
{
    fprintf(stderr, "Valid operations for '%s' are:\n", catalogList[idx].catalog);

    int i;
    for (i = 0; opTbls[idx][i].operation != NULL; i++) {
        struct opEntry entry = opTbls[idx][i];
        fprintf(stderr, "    %-12.12s%s\n", entry.operation, entry.description);
    }

    return;
} // end function print_operations
